/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Communication;

import Common.InfoLogger;
import Events.*;
import java.lang.Thread;
import java.net.*;
import java.io.*;
import Common.CONSTANTS;
import Response.ResponseMsgList;
import Request.ProbeRequest;
import Response.*;
import javax.swing.event.EventListenerList;


/**
 *
 * @author lcy email: liuchy35@gmail.com
 */

public class ProbeSockThread extends Thread implements ProbeEventHandler {

    private DatagramSocket _probe_sock;
    private int _port;
    private InfoLogger _debugger;
    private static final int _UDP_BUFFER_SIZE = CONSTANTS.UDP_PROBE_BUF_SIZE;
    private ObjectOutputStream out_channel;
    private ByteArrayOutputStream _out_buffer;

    private ByteArrayInputStream _in_buffer;
    private ObjectInputStream in_channel;
    private byte[] _recv_buf;


    //Event sections
    //The proberesp event
    private EventListenerList _probeRespListeners;

    public ProbeSockThread(){

        //Default port
        _port = 9010;
        InitCommComponent();
    }

    public ProbeSockThread(int port){
        _port = port;
        InitCommComponent();
    }

    private void InitCommComponent(){
        _debugger = new InfoLogger();
        _out_buffer = null;
        out_channel = null;
        _in_buffer = null;
        in_channel = null;
        _recv_buf = null;
        _probeRespListeners = new EventListenerList();
    }

    @Override
    public void run()
    {
        try{
            _probe_sock = new DatagramSocket(_port);
            _debugger.LogInfo("Server Probe Socket: probe socket opened");

            _recv_buf = new byte [_UDP_BUFFER_SIZE];
            
            DatagramPacket _recv_pack = new DatagramPacket(_recv_buf, _recv_buf.length);

            //Open input channel
            while(true){
                try{
                    _probe_sock.receive(_recv_pack);
                }catch(SocketTimeoutException ex){
		    //Do nothing now
		    continue;
		}
		catch(IOException e){
		    
                }
                //Process packet
		ProcessPacket(_recv_pack);
		   
		// Clear buffer resource for next receive
            }
        }catch(SocketException s_ex){
            _debugger.LogInfo("Server Probe socket: cannot open the probe socket");
            return;
        }
    }

    public boolean ProcessPacket(DatagramPacket packet){

	try{
	    _in_buffer = new ByteArrayInputStream(_recv_buf);
	    in_channel = new ObjectInputStream(new BufferedInputStream(_in_buffer));

            NetDataResponse response;
            
            try{
                response = (NetDataResponse)in_channel.readObject();
                _in_buffer.reset();
            }catch(ClassNotFoundException ex){
                return false;
            }

	    //If something else received, then return false;
	    if(response.GetRespType() != RespType.PROBE){
		return false;
	    }

	    String msg = (String)response.GetResultMsg();
	    if(!msg.equals(ResponseMsgList.msg_list[ResponseMsgList.PROBE_RESP_IDX]))
		return false;

            //TODO fire an event to inform;
            ProbeResponse probe_resp = (ProbeResponse) response;
            
            InetAddress cli_addr = packet.getAddress();
            int cli_port = packet.getPort();
            ProbeRespArg arg = new ProbeRespArg();

            arg.SetInetAddress(cli_addr);
            arg.SetPort(cli_port);
            arg.SetUserName((String)probe_resp.GetResponseContent());
            
            _debugger.LogInfo("Response Name: " + (String)probe_resp.GetResponseContent());

            ProbeRespReceived event = new ProbeRespReceived(this,arg);

            fireProbeRespEvent(event);

	}catch(IOException ex){
	    _debugger.LogInfo("ProbeProcess: Cannot open the input stream");
	}

	return true;
    }

    //http://www.javaworld.com/javatips/jw-javatip40.html?page=2
    //The probe event handler
    
    @Override
    public void SocketProbeOccurred(ProbeEvent ev){
        //TODO: send a probe message to remote address
        ProbeEventArg arg = ev.GetArg();

        _out_buffer = new ByteArrayOutputStream(_UDP_BUFFER_SIZE);

        try{

            //First buffer the probe message; make sure every piece of message
            //is less than _UDP_BUFFER_SIZE;
            out_channel = new ObjectOutputStream(new BufferedOutputStream(_out_buffer));
            out_channel.flush();
            ProbeRequest request = new ProbeRequest();
            request.SetMsg(ResponseMsgList.msg_list[ResponseMsgList.PROBE_MSG_IDX]);
            out_channel.writeObject(request);
            out_channel.flush();

            //Then sendout the datagram packet through the datagram socket;
            //retrieves byte array
            byte[] sendBuf = _out_buffer.toByteArray();
            DatagramPacket packet = new DatagramPacket(
                              sendBuf, sendBuf.length, 
                              InetAddress.getByName(CONSTANTS.UDP_MULTICAST_IP),
                              CONSTANTS.UDP_MULTICAST_PORT);

            _probe_sock.send(packet);
            _out_buffer.reset();
            
        }catch(IOException ex){
            //I.e. Cannot create the output stream;
            _debugger.LogInfo("Cannot create output stream");
            return ;
        }
    }

    public synchronized void addProbeRespHandler(ProbeRespHandler l){
        _probeRespListeners.add(ProbeRespHandler.class, l);
    }

    public synchronized void removeProbeRespHandler(ProbeRespHandler l){
       _probeRespListeners.remove(ProbeRespHandler.class, l);
    }

    public synchronized void fireProbeRespEvent(ProbeRespReceived eve){
         //TODO: analyse arg if we need in the future;
        Object[] listeners = _probeRespListeners.getListenerList();

        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i=0; i<listeners.length; i+=2) {
            if (listeners[i]==ProbeRespHandler.class) {
                ((ProbeRespHandler)listeners[i+1]).HandleProbeResp(eve);
            }
        }
    }
}
